//
// Created by haozhenghui on 2020/12/4.
//

#include "bit.h"
#include <string.h>
#include <stdio.h>

int bit_get(const unsigned char *bits, int pos) {
    unsigned char mask;
    int i;
    mask = 0x80;
    for (i = 0; i < (pos % 8); i++) {
        mask = mask >> 1;
    }
    return (((mask & bits[(int) (pos / 8)]) == mask) ? 1 : 0);
}

void bit_set(unsigned char *bits, int pos, int state) {
    unsigned char mask;
    int i;
    mask = 0x80;
    for (i = 0; i < (pos % 8); i++) {
        mask = mask >> 1;
    }
    if (state) {
        bits[pos / 8] = bits[pos / 8] | mask;
    } else {
        bits[pos / 8] = bits[pos / 8] & (~mask);
    }
    return;
}

void bit_xor(const unsigned char *bits1, const unsigned char *bits2, unsigned char *bitsx, int size) {
    int i;
    for (i = 0; i < size; i++) {
        if (bit_get(bits1, i) != bit_get(bits2, i)) {
            bit_set(bitsx, i, 1);
        } else {
            bit_set(bitsx, i, 0);
        }
    }
}

void bit_rot_left(unsigned char *bits, int size, int count) {
    int fbit, lbit, i, j;
    if (size > 0) {
        for (j = 0; j < count; j++) {
            for (i = 0; i <= ((size - 1) / 8); i++) {
                lbit = bit_get(&bits[i], 0);
                if (i == 0) {
                    fbit = lbit;
                } else {
                    bit_set(&bits[i - 1], 7, lbit);
                }
                bits[i] = bits[i] << 1;
            }
            bit_set(bits, size - 1, fbit);
        }
    }
}

int bit_test() {
    unsigned char bits1[8],
            bits2[8],
            bits3[8];

    int i;

/*****************************************************************************
*                                                                            *
*  Perform some bit operations using 64-bit buffers.                         *
*                                                                            *
*****************************************************************************/

    for (i = 0; i < 8; i++) {

        bits1[i] = 0x00;
        bits2[i] = 0x00;
        bits3[i] = 0x00;

    }

    fprintf(stdout, "Initially\n");

    fprintf(stdout, "bits1: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits1[0],
            bits1[1], bits1[2], bits1[3], bits1[4], bits1[5], bits1[6], bits1[7]);

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    bit_set(bits1, 15, 1);
    bit_set(bits1, 16, 1);
    bit_set(bits1, 32, 1);
    bit_set(bits1, 63, 1);
    bit_set(bits2, 0, 1);
    bit_set(bits2, 15, 1);

    fprintf(stdout, "After setting bits 15, 16, 32, 63 of bits1 and bits 00, 15 "
                    "of bits2\n");

    fprintf(stdout, "bits1: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits1[0],
            bits1[1], bits1[2], bits1[3], bits1[4], bits1[5], bits1[6], bits1[7]);

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    fprintf(stdout, "Bit 63 of bits1 is %d\n", bit_get(bits1, 63));
    fprintf(stdout, "Bit 62 of bits1 is %d\n", bit_get(bits1, 62));
    fprintf(stdout, "Bit 00 of bits2 is %d\n", bit_get(bits2, 0));
    fprintf(stdout, "Bit 01 of bits2 is %d\n", bit_get(bits2, 1));

    bit_xor(bits1, bits2, bits3, 32);

    fprintf(stdout, "bits3 is bits1 XOR bits2 (32 bits)\n");

    fprintf(stdout, "bits3: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits3[0],
            bits3[1], bits3[2], bits3[3], bits3[4], bits3[5], bits3[6], bits3[7]);

    bit_xor(bits1, bits2, bits3, 64);

    fprintf(stdout, "bits3 is bits1 XOR bits2 (64 bits)\n");

    fprintf(stdout, "bits3: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits3[0],
            bits3[1], bits3[2], bits3[3], bits3[4], bits3[5], bits3[6], bits3[7]);

    bit_rot_left(bits1, 64, 1);

    fprintf(stdout, "After rotating bits1 left x 1 (64 bits)\n");

    fprintf(stdout, "bits1: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits1[0],
            bits1[1], bits1[2], bits1[3], bits1[4], bits1[5], bits1[6], bits1[7]);

    bit_rot_left(bits2, 64, 1);

    fprintf(stdout, "After rotating bits2 left x 1 (64 bits)\n");

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    bit_rot_left(bits2, 16, 7);

    fprintf(stdout, "After rotating bits2 left x 7 (16 bits)\n");

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    bit_rot_left(bits2, 8, 2);

    fprintf(stdout, "After rotating bits2 left x 2 (8 bits)\n");

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    for (i = 0; i < 8; i++) {

        bits2[i] = 0x00;

    }

    bit_set(bits2, 0, 1);
    bit_set(bits2, 3, 1);
    bit_set(bits2, 8, 1);
    bit_set(bits2, 27, 1);

    fprintf(stdout, "After clearing and setting bits 0, 3, 8, 27 of bits2\n");

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    bit_rot_left(bits2, 11, 6);

    fprintf(stdout, "After rotating bits2 left x 6 (11 bits)\n");

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    for (i = 0; i < 8; i++) {

        bits2[i] = 0x00;

    }

    bit_set(bits2, 0, 1);
    bit_set(bits2, 3, 1);
    bit_set(bits2, 8, 1);
    bit_set(bits2, 27, 1);

    fprintf(stdout, "After clearing and setting bits 0, 3, 8, 27 of bits2\n");

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    bit_rot_left(bits2, 28, 4);

    fprintf(stdout, "After rotating bits2 left x 4 (28 bits)\n");

    fprintf(stdout, "bits2: %02x %02x %02x %02x %02x %02x %02x %02x\n", bits2[0],
            bits2[1], bits2[2], bits2[3], bits2[4], bits2[5], bits2[6], bits2[7]);

    return 0;

}
